package com.ustc.hewei.nettyrpc2_0.netty.client.loadBalance.impl;

import com.ustc.hewei.nettyrpc2_0.common.warp.ZkNode;
import com.ustc.hewei.nettyrpc2_0.netty.client.loadBalance.CommonLoadBalance;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

/**
* @description: 基于权重的随机负载均衡(Nacos)
* @author hewei
* @date 2022/9/21 09:30
* @version 1.0
*/

public class RandomWithWeight implements CommonLoadBalance {
    @Override
    public ZkNode getService(List<ZkNode> zkNodeList) {
        if (zkNodeList == null || zkNodeList.size() <= 0) {
            return null;
        }
        double weightSum = 0;
        List<ZkNode> goodNodeList = new ArrayList<>();
        for (ZkNode zkNode : zkNodeList) {
            int weight = zkNode.getWeight();
            if (weight <= 0) continue;
            goodNodeList.add(zkNode);
            weightSum += weight;
        }
        double[] extraWeights = new double[goodNodeList.size()];
        double startPos = 0d;
        for (int i = 0; i < extraWeights.length; i++) {
            double actualWeight = goodNodeList.get(i).getWeight() / weightSum;
            extraWeights[i] = startPos + actualWeight;
            startPos += actualWeight;
        }
        double random = ThreadLocalRandom.current().nextDouble(0, 1);

        /*
         * 解释一下binarySearch这个方法
         * 它是在数组中查找目标值target，如果找到了，返回的就是就是对应的数在数组中的下标，
         * 如果没找到，它会返回target理应在数组中的位置下标，但是这样就会和找到target的情况重合，
         * 所以如果找到的是应该插入的下标，则会返回(-(insertion point)-1)，
         * 所以想要取出应该插入的位置，就是（-index - 1）
         **/
        int index = Arrays.binarySearch(extraWeights, random);
        if (index < 0) {
            index = -index - 1;
        } else {
            return goodNodeList.get(index);
        }
        if (index < extraWeights.length && random< extraWeights[index]) {
            return goodNodeList.get(index);
        }
        return goodNodeList.get(0);
    }
}
